import { ObjectSystemAttribute, FriendOf, ExpandOf, GetTypeOf } from "../../../../../Decorator/Decorator";
import { FUIComponent } from "../../../../ModelView/Client/Plugins/FairyGUI/FUIComponent";
import { AwakeSystem } from "../../../../../Core/Module/EventSystem/IAwakeSystem";
import { DestroySystem } from "../../../../../Core/Module/EventSystem/IDestroySystem";
import { PanelId } from "../../../../ModelView/Client/Demo/FUIAutoGen/PanelId";
import { Entity } from "../../../../../Core/Module/Entity/Entity";
import { FUIEntity } from "../../../../ModelView/Client/Plugins/FairyGUI/FUIEntity";
import { Log } from "../../../../../Core/Module/Log/Log";
import { FUIEventComponent } from "../../../../ModelView/Client/Plugins/FairyGUI/FUIEventComponent";
import { CoroutineLock } from "../../../../../Core/Module/CoroutineLock/CoroutineLock";
import { CoroutineLockComponent } from "../../../../../Core/Module/CoroutineLock/CoroutineLockComponent";
import { CoroutineLockType } from "../../../../../Core/Module/CoroutineLock/CoroutineLockType";
import { UIPanelType } from "../../../../ModelView/Client/Plugins/FairyGUI/PanelDataDefine";
import { ETTask } from "../../../../../Support/Share/Lib/Async/ETTask";
import { FUIAssetComponent } from "../../../../ModelView/Client/Plugins/FairyGUI/FUIAssetComponent";
import { FUIRootHelper } from "./FUIRootHelper";
import { FUIBinder } from "../../../../ModelView/Client/Demo/FUIAutoGen/FUIBinder";

/**
 * 使用 Expand 扩展方法
 * 
 * 必须作出以下申明，才能调用对象的扩展方法
 */
declare module "../../../../ModelView/Client/Plugins/FairyGUI/FUIComponent" {

    interface FUIComponent {
        Awake(): void;
        Destroy(): void;
        Restart(): void;
        IsPanelVisible(id: PanelId): boolean;
        HideLastPanel(): void;
        CloseLastPanel(): void;
        ShowPanelStackAsync(id: PanelId): Promise<void>;
        ShowPanelAsync(id: PanelId);
        ShowPanelAsyncWith<T extends Entity>(id: PanelId, contextData: T): Promise<void>;
        HideAndShowPanelStackAsync(hidePanelId: PanelId, showPanelId: PanelId, contextData?: Entity);
        HidePanel(id: PanelId): void;
        HideAndPopPanelStack(id: PanelId): void;
        UnLoadPanel(id: PanelId, isDispose?: boolean): void;
        ShowFUIEntityAsync(id: PanelId): Promise<FUIEntity>;
        GetFUIEntity(id: PanelId): FUIEntity;
        GetPanelLogic<T extends Entity>(clazz: typeof Entity, isNeedShowState: boolean): T;
        GetPanelIdByGeneric<T extends Entity>(clazz: typeof Entity): PanelId;
        ClosePanel(id: PanelId): void;
        CloseAllPanel(): void;
        HideAllShownPanel(includeFixed: boolean): void;
        RealShowPanel(fuiEntity: FUIEntity, id: PanelId, contextData?: Entity): void;
        CheckDirectlyHide(id: PanelId): boolean;
        LoadFUIEntitysAsync(fuiEntity: FUIEntity);
        CreateObjectAsync(packageName: string, componentName: string): Promise<fgui.GComponent>;
    }

}

@ExpandOf(FUIComponent)
export class FUIComponentSystem {

    private static Awake(self: FUIComponent): void {
        self.AllPanelsDic?.Clear();
        self.VisiblePanelsDic?.Clear();
        self.VisiblePanelsQueue?.Clear();
        self.HidePanelsStack?.Clear();

        FUIBinder.BindAll();
    }

    private static Destroy(self: FUIComponent): void {
        self.CloseAllPanel();
    }

    private static Restart(self: FUIComponent): void {
        self.CloseAllPanel();

        FUIBinder.BindAll();
    }

    /// <summary>
    /// 窗口是否是正在显示的 
    /// </summary>
    /// <OtherParam name="id"></OtherParam>
    /// <returns></returns>
    private static IsPanelVisible(self: FUIComponent, id: PanelId): boolean {
        return self.VisiblePanelsDic.ContainsKey(id);
    }

    /// <summary>
    /// 隐藏最新出现的窗口
    /// </summary>
    private static HideLastPanel(self: FUIComponent): void {
        if (self.VisiblePanelsQueue.Count <= 0) {
            return;
        }
        const PanelId = self.VisiblePanelsQueue[self.VisiblePanelsQueue.Count - 1];
        if (!self.IsPanelVisible(PanelId)) {
            return;
        }
        self.HidePanel(PanelId);
    }

    /// <summary>
    /// 彻底关闭最新出现的窗口
    /// </summary>
    private static CloseLastPanel(self: FUIComponent): void {
        if (self.VisiblePanelsQueue.Count <= 0) {
            return;
        }
        const PanelId = self.VisiblePanelsQueue[self.VisiblePanelsQueue.Count - 1];
        if (!self.IsPanelVisible(PanelId)) {
            return;
        }
        self.ClosePanel(PanelId);
    }

    private static async ShowPanelStackAsync(self: FUIComponent, id: PanelId): Promise<void> {
        const fuiEntity = await self.ShowFUIEntityAsync(id);
        if (fuiEntity == null) {
            return;
        }
        self.ShowPanelAsyncWith(id, fuiEntity.ContextData).then();
    }

    /// <summary>
    /// 显示界面。没有 contextData 的重载
    /// </summary>
    private static async ShowPanelAsync(self: FUIComponent, id: PanelId) {
        try {
            const fuiEntity = await self.ShowFUIEntityAsync(id);
            if (fuiEntity != null) {
                self.RealShowPanel(fuiEntity, id);
            }
        } catch (e) {
            Log.Error(e);
        }
    }

    /// <summary>
    /// 显示界面
    /// </summary>
    private static async ShowPanelAsyncWith<T extends Entity>(self: FUIComponent, id: PanelId, contextData: T): Promise<void> {
        try {
            const fuiEntity = await self.ShowFUIEntityAsync(id);
            if (fuiEntity != null) {
                if (contextData != null) {
                    fuiEntity.AddChild(contextData);
                    fuiEntity.ContextData = contextData;
                }
                self.RealShowPanel(fuiEntity, id, contextData);
            }
        } catch (e) {
            Log.Error(e);
        } finally {
            if (contextData != null) {
                contextData.Dispose();
            }
        }
    }

    /// <summary>
    /// 隐藏 hidePanelId 界面，然后显示 showPanelId 界面。并将 hidePanelId 界面压入栈中。
    /// </summary>
    /// <param name="self"></param>
    /// <param name="hidePanelId">要隐藏的界面Id</param>
    /// <param name="showPanelId">要显示的界面Id</param>
    /// <param name="contextData">界面参数</param>
    private static async HideAndShowPanelStackAsync(self: FUIComponent, hidePanelId: PanelId, showPanelId: PanelId, contextData: Entity = null) {
        // 隐藏 hidePanelId
        if (!self.CheckDirectlyHide(hidePanelId)) {
            Log.Warning(`检测关闭 panelId: ${hidePanelId} 失败！`);
        }

        // 显示 showPanelId
        await self.ShowPanelAsyncWith(showPanelId, contextData);

        const fuiEntity = self.GetFUIEntity(showPanelId);
        if (fuiEntity == null) {
            Log.Error(`界面 ${showPanelId} 创建失败！`);
            return;
        }

        // 将 hidePanelId 界面压入栈中
        if (hidePanelId != PanelId.Invalid) {
            self.HidePanelsStack.Push(hidePanelId);
            fuiEntity.IsUsingStack = true;
        }
    }

    /// <summary>
    /// 隐藏ID指定的UI窗口。如果之前使用 HideAndShowPanelStackAsync() 显示，则调用 HideAndPopPanelStack()，否则调用 CheckDirectlyHide()。
    /// </summary>
    /// <OtherParam name="id"></OtherParam>
    private static HidePanel(self: FUIComponent, id: PanelId): void {
        const fuiEntity = self.VisiblePanelsDic.TryGetValue(id);
        if (!fuiEntity) {
            return;
        }
        if (fuiEntity.IsUsingStack) {
            self.HideAndPopPanelStack(id);
        } else {
            self.CheckDirectlyHide(id);
        }
    }

    private static HideAndPopPanelStack(self: FUIComponent, id: PanelId): void {
        if (!self.CheckDirectlyHide(id)) {
            Log.Warning(`检测关闭 panelId: ${id} 失败！`);
            return;
        }

        if (self.HidePanelsStack.Count <= 0) {
            return;
        }

        const prePanelId = self.HidePanelsStack.Pop();
        self.ShowPanelStackAsync(prePanelId).then();
    }

    /// <summary>
    /// 卸载指定的UI窗口实例
    /// </summary>
    /// <OtherParam name="id"></OtherParam>
    private static UnLoadPanel(self: FUIComponent, id: PanelId, isDispose: boolean = true): void {
        const fuiEntity = self.GetFUIEntity(id);
        if (null == fuiEntity) {
            Log.Error(`FUIEntity PanelId {id} is null!!!`);
            return;
        }

        const handler = FUIEventComponent.Instance.GetUIEventHandler(id);
        handler.BeforeUnload(fuiEntity);
        if (fuiEntity.IsPreLoad) {
            fuiEntity.GComponent.dispose();
            fuiEntity.GComponent = null;
        }

        if (isDispose) {
            self.AllPanelsDic.Remove(id);
            self.VisiblePanelsDic.Remove(id);
            self.VisiblePanelsQueue.Remove(id);
            fuiEntity?.Dispose();
        }
    }

    private static async ShowFUIEntityAsync(self: FUIComponent, id: PanelId): Promise<FUIEntity> {
        let coroutineLock: CoroutineLock = null;
        try {
            coroutineLock = await CoroutineLockComponent.Instance.Wait(CoroutineLockType.LoadingPanels, id);
            let fuiEntity = self.GetFUIEntity(id);
            // 如果UI不存在开始实例化新的窗口
            if (null == fuiEntity) {
                fuiEntity = self.AddChild<FUIEntity>(FUIEntity);
                fuiEntity.PanelId = id;
                await self.LoadFUIEntitysAsync(fuiEntity);
            }
            if (!fuiEntity.IsPreLoad) {
                await self.LoadFUIEntitysAsync(fuiEntity);
            }
            return fuiEntity;
        } catch (e) {
            throw e;
        } finally {
            coroutineLock?.Dispose();
        }
    }

    private static GetFUIEntity(self: FUIComponent, id: PanelId): FUIEntity {
        const fuiEntity = self.AllPanelsDic.TryGetValue(id);
        if (fuiEntity) {
            return fuiEntity;
        }
        return null;
    }

    private static GetPanelLogic<T extends Entity>(self: FUIComponent, clazz: typeof Entity, isNeedShowState: boolean = false): T {
        const panelId = self.GetPanelIdByGeneric<T>(clazz);
        const fuiEntity = self.GetFUIEntity(panelId);
        if (null == fuiEntity) {
            Log.Warning(`${panelId} is not created!`);
            return null;
        }

        if (!fuiEntity.IsPreLoad) {
            Log.Warning(`${panelId} is not loaded!`);
            return null;
        }

        if (isNeedShowState) {
            if (!self.IsPanelVisible(panelId)) {
                Log.Warning(`${panelId} is need show state!`);
                return null;
            }
        }
        return fuiEntity.GetComponent<T>(clazz);
    }

    private static GetPanelIdByGeneric<T extends Entity>(self: FUIComponent, clazz: typeof Entity): PanelId {
        const panelInfo = FUIEventComponent.Instance.PanelTypeInfoDict.TryGetValue(GetTypeOf(clazz));
        if (panelInfo) {
            return panelInfo.PanelId;
        }
        Log.Error(`${GetTypeOf(clazz)} is not have any PanelId!`);
        return PanelId.Invalid;
    }

    /// <summary>
    /// 关闭指定的UI窗口，会Unload资源。
    /// </summary>
    /// <param name="self"></param>
    /// <param name="PanelId"></param>
    private static ClosePanel(self: FUIComponent, id: PanelId): void {
        if (!self.VisiblePanelsDic.ContainsKey(id)) {
            return;
        }
        self.HidePanel(id);
        self.UnLoadPanel(id);
        Log.Info("<color=magenta>## close panel without Pop ##</color>");
    }

    private static CloseAllPanel(self: FUIComponent): void {
        if (self.AllPanelsDic == null) {
            return;
        }
        for (const key in self.AllPanelsDic) {
            const panel = self.AllPanelsDic[key];
            const fuiEntity = panel.Value;
            if (fuiEntity == null || fuiEntity.IsDisposed) {
                continue;
            }
            self.CheckDirectlyHide(fuiEntity.PanelId);
            self.UnLoadPanel(fuiEntity.PanelId, false);
            fuiEntity?.Dispose();
        }
        self.VisiblePanelsDic.Clear();
        self.AllPanelsDic.Clear();
        self.FUIEntitylistCached.Clear();
        self.VisiblePanelsQueue.Clear();
        self.HidePanelsStack.Clear();
    }

    private static HideAllShownPanel(self: FUIComponent, includeFixed: boolean = false): void {
        self.FUIEntitylistCached.Clear();
        for (const key in self.VisiblePanelsDic) {
            const panel = self.VisiblePanelsDic[key];
            if (panel.Value.PanelCoreData.panelType == UIPanelType.Fixed && !includeFixed) {
                continue;
            }
            if (panel.Value.IsDisposed) {
                continue;
            }
            self.FUIEntitylistCached.Add(panel.Key);
            panel.Value.GComponent.visible = false;
            FUIEventComponent.Instance.GetUIEventHandler(panel.Value.PanelId).OnHide(panel.Value);
        }
        if (self.FUIEntitylistCached.Count > 0) {
            for (let i = 0; i < self.FUIEntitylistCached.Count; i++) {
                self.VisiblePanelsDic.Remove(self.FUIEntitylistCached[i]);
            }
        }
        self.VisiblePanelsQueue.Clear();
        self.HidePanelsStack.Clear();
    }

    private static RealShowPanel(self: FUIComponent, fuiEntity: FUIEntity, id: PanelId, contextData: Entity = null): void {
        if (fuiEntity.PanelCoreData.panelType == UIPanelType.PopUp) {
            self.VisiblePanelsQueue.Add(id);
        }
        fuiEntity.GComponent.visible = true;
        self.VisiblePanelsDic.Add(id, fuiEntity);
        FUIEventComponent.Instance.GetUIEventHandler(id).OnShow(fuiEntity, contextData);
        Log.Info(`<color=magenta>### current Navigation panel </color>${fuiEntity.PanelId}`);
    }

    private static CheckDirectlyHide(self: FUIComponent, id: PanelId): boolean {
        const fuiEntity = self.VisiblePanelsDic.TryGetValue(id);
        if (!fuiEntity) {
            return false;
        }
        if (fuiEntity != null && !fuiEntity.IsDisposed) {
            fuiEntity.GComponent.visible = false;
            FUIEventComponent.Instance.GetUIEventHandler(id).OnHide(fuiEntity);
        }
        self.VisiblePanelsDic.Remove(id);
        self.VisiblePanelsQueue.Remove(id);
        return true;
    }

    /// <summary>
    /// 异步加载
    /// </summary>
    private static async LoadFUIEntitysAsync(self: FUIComponent, fuiEntity: FUIEntity) {
        const panelInfo = FUIEventComponent.Instance.PanelIdInfoDict.TryGetValue(fuiEntity.PanelId);
        if (!panelInfo) {
            Log.Error(`${fuiEntity.PanelId} panelInfo is not Exist!`);
            return;
        }
        const fuiEventHandler = FUIEventComponent.Instance.GetUIEventHandler(fuiEntity.PanelId);
        // 添加Package
        const uiPackage = await self.ClientScene().GetComponent<FUIAssetComponent>(FUIAssetComponent).LoadUIPackageAsync(panelInfo.PackageName);
        if (uiPackage == null) {
            Log.Error(`UIPackage ${panelInfo.PackageName} load failed!`);
            return;
        }
        // 创建组件
        fuiEntity.GComponent = await self.CreateObjectAsync(panelInfo.PackageName, panelInfo.ComponentName);
        fuiEventHandler.OnInitPanelCoreData(fuiEntity);
        // 设置根节点
        fuiEntity.SetRoot(FUIRootHelper.GetTargetRoot(fuiEntity.PanelCoreData.panelType));
        fuiEventHandler.OnInitComponent(fuiEntity);
        fuiEventHandler.OnRegisterUIEvent(fuiEntity);
        self.AllPanelsDic[fuiEntity.PanelId] = fuiEntity;
    }

    private static async CreateObjectAsync(self: FUIComponent, packageName: string, componentName: string): Promise<fgui.GComponent> {
        const task = ETTask.Create<fgui.GComponent>();
        // const fguiObject = fgui.UIPackage.createObject(packageName, componentName);
        // task.SetResult(fguiObject.asCom);
        const handler = new fgui.AsyncOperation();
        handler.callback = (result) => {
            task.SetResult(result.asCom);
        };
        handler.createObject(packageName, componentName);
        return await task;
    }

}

@FriendOf(FUIComponent)
@ObjectSystemAttribute()
export class FUIComponentAwakeSystem extends AwakeSystem<FUIComponent> {

    Awake(self: FUIComponent): void {
        self.Awake();
    }

}

@FriendOf(FUIComponent)
@ObjectSystemAttribute()
export class FUIComponentDestroySystem extends DestroySystem<FUIComponent> {

    Destroy(self: FUIComponent): void {
        self.Destroy();
    }

}